//-*-Igor-*-
// ###################################################################
//  Igor Pro - JEG Image Tools
// 
//  FILE: "JEG Image Statistics"
//                                    created: 8/1/98 {4:19:18 PM} 
//                                last update: 8/2/98 {12:09:35 PM} 
//  Author: Jonathan Guyer
//  E-mail: <jguyer@his.com>
//     www: <http://www.his.com/~jguyer/>
//      
//  These routines add a "Roughness" item to the Analysis menu. 
//  Selection of this item, allows the user to select an image from 
//  the topmost graph [this needs to be more robust]. A panel is 
//  then displayed which shows several commonly cited (and virtually 
//  meaningless) statistics for surface morphologies, such as calculated 
//  by the NanoScope software. If a Marquee is drawn out in the image, 
//  the statistics panel will be automatically updated to reflect the 
//  values for the region within the marquee. Removing the Marquee does not
//  trigger an update (I have no control over this), but you can click
//  the "Full Image" button, in this event. If the Marquee lies outside the
//  image, the statistics calculated will be for the nearest edge.
//  
//  The statistical values are displayed with the data dimension units 
//  of the image wave, if available. Moreover, if the wave is of integer 
//  type, these routines will attempt to apply the integer scaling 
//  described in JEG Color Legend. 
//  
//  Because Igor's string formatting routines rigorously apply SI prefixes 
//  (I guess, now that I work for NIST, I should praise them for this), 
//  rather than "1.000 " or "0.100 nm", the panel will display "100.0 pm". 
//  Igor has no way of knowing that most humans don't use picometers.
//  
//  Note: If the data dimension units are changed after the statistics
//  panel is displayed, the change will not be reflected in the panel. 
//  Simply create a new statistics panel.
// 
//  I'd be happy to add other statistical measures, if requested. For 
//  instance, it should be trivial to add NanoScope's Rmax if anybody can 
//  explain to me what on earth "This is the difference in height between
//  the highest and lowest points on the surface relative to the Mean Plane" 
//  means. How can a difference be relative to anything?
// 
//  modified by  rev reason
//  -------- --- --- -----------
//  8/1/98   JEG 1.0 original
// ###################################################################
// 

#include "JEG Wave Tools"
#include <Strings as Lists>
#include <Keyword-Value>

#pragma rtGlobals=1		// Use modern global access method.

// -----------Menu routines----------------------------------------

Menu "Analysis"
	"Roughness", JEG_ImageStatistics()
End 

// 
// -------------------------------------------------------------------------
// 
// "JEG_ImageStatistics" --
// 
//  Gateway procedure to prompt the user for an image for which to calculate
//  statistics.
// -------------------------------------------------------------------------
// 
Proc JEG_ImageStatistics(image)
	String image
	Prompt image,"Image wave:", Popup ImageNameList("",";")
	
	Silent 1; DelayUpdate
	
	JEG_MakeStatsPanel(ImageNameToWaveRef("",image))
End

// 
// -------------------------------------------------------------------------
// 
// "JEG_MakeStatsPanel" --
// 
//  Draw a panel which will show automatically updated statistics for 
//  'image'.
// -------------------------------------------------------------------------
// 
Function JEG_MakeStatsPanel(image)
	Wave image

	String dfSav = GetDataFolder(1)
	SetDataFolder root:
	
	// So they'll be updated later by the Marquee
	Variable/G V_marquee = 1, V_Flag, V_left, V_right, V_top, V_bottom
	
	NewDataFolder/O/S root:Packages
	NewDataFolder/O/S 'JEG Image Statistics'
	// Get a new, unique datafolder for this set of statistics
	String folder = UniqueName("stats",11,0)
	NewDataFolder/S $folder
	
	String/G imagePath = GetWavesDataFolder(image,2)
	Variable/G Zrange, Zmean, Rq, Ra
	
	String df = GetDataFolder(1)
	
	// Set up an artificial dependency on the Marquee
	Variable/G dummy
	String s = "dummy := JEG_UpdateStats(\"" + df + "\", "
	s += "root:V_left, root:V_right, root:V_top, root:V_bottom)"
	Execute s
	
	String units = WaveUnits(image,-1)
	
	NewPanel /W=(543,106,809,191) as NameOfWave(image) + " Statistics"
	SetDrawLayer ProgBack
	ValDisplay valdisp_Zrange,pos={87,5},size={173,17},title="Z range"
	ValDisplay valdisp_Zrange,format="%W1P" + units
	ValDisplay valdisp_Zrange,limits={0,0,0},barmisc={0,1000}
	Execute "ValDisplay valdisp_Zrange,value= " + df + "Zrange"
	ValDisplay valdisp_Zmean,pos={102,24},size={158,17},title="Mean"
	ValDisplay valdisp_Zmean,format="%W1P" + units
	ValDisplay valdisp_Zmean,limits={0,0,0},barmisc={0,1000}
	Execute "ValDisplay valdisp_Zmean, value= " + df + "Zmean"
	ValDisplay valdisp_Rq,pos={10,43},size={250,17},title="RMS Roughness (Rq)"
	ValDisplay valdisp_Rq,limits={0,0,0},barmisc={0,1000}
	ValDisplay valdisp_Rq,format="%W1P" + units
	Execute "ValDisplay valdisp_Rq,value= " + df + "Rq"
	ValDisplay valdisp_Ra,pos={5,62},size={255,17},title="Mean Roughness (Ra)"
	ValDisplay valdisp_Ra,limits={0,0,0},barmisc={0,1000}
	ValDisplay valdisp_Ra,format="%W1P" + units
	Execute "ValDisplay valdisp_Ra,value= " + df + "Ra"

	Button $("button_" + folder),pos={4,15},size={80,20},title="Full Image",proc=JEG_FullImageStatsProc

	SetDataFolder dfSav
End

// 
// -------------------------------------------------------------------------
// 
// "JEG_FullImageStatsProc" --
// 
//  Ignore the Marquee and calculate statistics for the entire image.
// -------------------------------------------------------------------------
// 
Function JEG_FullImageStatsProc(ctrlName) : ButtonControl
	String ctrlName
	// ctrlName must be "button_" + statsName
						// strip leading "button_"
	String statsName = ctrlName[7,strlen(ctrlName)-1]
	
	String dfSav = GetDataFolder(1)
	SetDataFolder root:Packages:'JEG Image Statistics'
	SetDataFolder statsName
	
	SVAR imagePath = imagePath
	
	Duplicate/O $imagePath, subset
	
	JEG_CalculateStats()
	
	SetDataFolder dfSav
End

// 
// -------------------------------------------------------------------------
// 
// "JEG_UpdateStats" --
// 
//  Automatically calculate image statistics. Triggered by changes in the
//  Marquee tool.
// -------------------------------------------------------------------------
// 
Function JEG_UpdateStats(statdf,left,right,top,bottom)
	String statdf
	Variable left, right, top, bottom
	
	// copy the marqueed image subset into our private directory for processing
	String dfSav = GetDataFolder(1)
	SetDataFolder statdf

	// Check if image is in the topmost graph
	String anImage, theList = ImageNameList("",";")
	SVAR imagePath = imagePath
	Variable i = 0
	do
		anImage = GetStrFromList(theList, i, ";")
		if (strlen(anImage) <= 0)
			break
		endif
		
		if (cmpstr(GetWavesDataFolder(ImageNameToWaveRef("",anImage),2), imagePath) == 0)
			String info = ImageInfo("",anImage,0)
			break
		endif
		
		i += 1
	while (1)
	
	// Proceed only if the image assosciated with this statistics folder 
	// was in the topmost graph
	if (strlen(anImage) != 0)
		String vaxis,haxis,df,xwave,ywave
		vaxis=StrByKey("YAXIS",info)
		haxis=StrByKey("XAXIS",info)
		df=StrByKey("ZWAVEDF",info)
		xwave=StrByKey("XWAVE",info)
		ywave=StrByKey("YWAVE",info)
		if( strlen(xwave)+strlen(ywave) )
			Abort "CopyImageSubset does not work on images with X or Y waves"
		endif
		string modInfo=StrByKey("RECREATION",info)
		info= AxisInfo("",haxis)
		String axisFlags= StrByKey("AXFLAG",info)
		info= AxisInfo("",vaxis)
		axisFlags+=StrByKey("AXFLAG",info)
		String winStyle= WinRecreation("",1)
		Variable swapxy= strsearch(winStyle,"swapXY=1",0) >= 0

		if (WinType("") != 1)
			Variable V_Flag = 1
		else
			GetMarquee $haxis,$vaxis
			left = V_left
			right = V_right
			top = V_top
			bottom = V_bottom
		endif
		
		if( V_Flag != 1)
			Duplicate/O $imagePath, subset
		else
			Make/O/N=2 subset
			JEG_CopyMatrix($imagePath,subset,left,right,top,bottom,swapxy)
		endif
				
		JEG_CalculateStats()
	endif
		
	SetDataFolder dfSav
End

// 
// -------------------------------------------------------------------------
// 
// "JEG_CalculateStats" --
// 
//  Actually perform the statistical calculations on the wave 'subset'.
// -------------------------------------------------------------------------
// 
Function JEG_CalculateStats()
	Wave subset = subset
	
	WaveStats/Q subset

	Variable zScale = 1
	String   units = ""
	
	if (JEG_WaveIsIntegral(subset) %& JEG_WaveIsScaled(subset))
		String info = WaveInfo(subset,0)
		String fullScale = JEG_StrByKey("FULLSCALE",info,":",";")
		zScale  = str2num(GetStrFromList(fullScale,2,","))		// data max full scale 
		zScale -= str2num(GetStrFromList(fullScale,1,","))		// data min full scale
		zScale /= 2^JEG_NumByKey("NUMTYPE",info,":",";")		// 2^8, 2^16, or 2^32
	endif
	
	NVAR Zrange = Zrange
	NVAR Zmean = Zmean
	NVAR Rq = Rq
	NVAR Ra = Ra

	Zrange = (V_max - V_min) * zScale
	Zmean = V_avg * zScale
	Rq = V_sdev * zScale
	Ra = V_adev * zScale
	
	KillWaves subset
End


// 
// -------------------------------------------------------------------------
// 
// "JEG_CopyMatrix" --
// 
//  Extract the image contained within the Marquee. Just like WaveMetrics'
//  'CopyMatrix' routine, except it's a Function and takes wave references
//  as arguments, instead of paths.
// -------------------------------------------------------------------------
// 
Function JEG_CopyMatrix(inmatrix,outmatrix,left,right,top,bottom,swapxy)
	Wave inmatrix,outmatrix
	Variable left,right,top,bottom,swapxy
	
	if( swapxy )
		Duplicate/O/R=(bottom,top)(left,right) inmatrix,outmatrix
	else
		Duplicate/O/R=(left,right)(bottom,top) inmatrix,outmatrix
	endif
End